import json
import sys
import socket
import struct
import time
import csv

from PyQt5.QtWidgets import QWidget, QApplication, QListWidgetItem
from WarningDialog import Ui_Form as ErrorDialog
from FrontLogIn import Ui_Form as FrontLogInUi
from FrontController import Ui_Form as FrontControlPanelUi
from FrontRoomAssign import Ui_Form as FrontAssignUi
from FrontCheckOut import Ui_Form as FrontCheckOut


class DialogWidget(QWidget):
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        super().__init__()

    def closeEvent(self, evt) -> None:
        # Todo: Revoke the disabled window element
        self.client.login.logInWindow.setEnabled(True)
        self.client.checkOut.checkOutWindow.setEnabled(True)
        pass


class AssignWidget(QWidget):
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        super().__init__()

    def closeEvent(self, evt) -> None:
        assert isinstance(self.client.panel, ClientControlPanel)
        # Todo:
        pass


class ClientDialog:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.errorDialogWindow = DialogWidget(client)
        self.errorDialogWindowContent = ErrorDialog()
        self.errorDialogWindowContent.setupUi(self.errorDialogWindow)
        self.errorDialogWindowContent.pushButton.clicked.connect(self.errorDialogWindow.close)

    def show_dialog(self, caution_str):
        assert isinstance(caution_str, str)
        self.errorDialogWindowContent.label.setText(
            "<html><head/><body><p align=\"center\"><span style=\" font-weight:600;\">" + caution_str + "</span></p></body></html>")
        self.errorDialogWindow.show()


class SubPanelWidget(QWidget):
    def __init__(self, client):
        super().__init__()
        assert isinstance(client, Client)
        self.client = client

    def closeEvent(self, evt) -> None:
        self.client.panel.controlPanel.setEnabled(True)
        pass


class ClientCheckOut:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.checkOutWindow = SubPanelWidget(client)
        self.checkOutWindowContent = FrontCheckOut()
        self.checkOutWindowContent.setupUi(self.checkOutWindow)

        self.checkOutWindowContent.showBillButton.clicked.connect(self.show_bill)
        self.checkOutWindowContent.showDetailButton.clicked.connect(self.show_details)
        self.checkOutWindowContent.checkButton.clicked.connect(self.confirm_check_out)
        self.checkOutWindowContent.saveButton.clicked.connect(self.save_report)

    def show_window(self):
        self.checkOutWindow.show()

    def show_details(self):
        # Todo:
        self.checkOutWindowContent.listWidget.clear()
        room_id = int(self.checkOutWindowContent.lineEdit.text())
        try:
            self.client.panel.socketToServer.recv(1024, socket.MSG_DONTWAIT)
        except BlockingIOError:
            pass
        self.client.panel.socketToServer.send(struct.pack('!ii', 2, room_id))
        data = self.client.panel.socketToServer.recv(8192)
        length = struct.unpack_from('!i', data, 0)
        if int(length[0]) == 0:
            self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("No valid details to show yet..."))
        else:
            fmt_str = "!" + str(length[0]) + 's'
            json_info = struct.unpack_from(fmt_str, data, 4)[0]
            info_list = json.loads(json_info)
            if len(info_list) == 0:
                self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("No valid details to show yet..."))
            else:
                for i in range(0, len(info_list)):
                    if i % 9 == 0:
                        self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("Detail " + str(int(i / 9))))
                        self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("Room ID: " + str(info_list[0])))
                    elif i % 9 == 1:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem("Fan Speed: " + str(info_list[1])))
                    elif i % 9 == 2:
                        self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("Fee Rate: " + str(info_list[2])))
                    elif i % 9 == 3:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem("Accumulated Fee: " + str(info_list[3])))
                    elif i % 9 == 4:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem(
                                "Submit Time: " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(info_list[4]))))
                    elif i % 9 == 5:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem(
                                "Start Time: " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(info_list[5]))))
                    elif i % 9 == 6:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem(
                                "End Time: " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(info_list[6]))))
                    elif i % 9 == 7:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem("Start Temperature: " + str(info_list[7])))
                    elif i % 9 == 8:
                        self.checkOutWindowContent.listWidget.addItem(
                            QListWidgetItem("End Temperature: " + str(info_list[8])))

    def show_bill(self):
        # Todo:
        self.checkOutWindowContent.listWidget.clear()
        room_id = int(self.checkOutWindowContent.lineEdit.text())
        try:
            self.client.panel.socketToServer.recv(1024, socket.MSG_DONTWAIT)
        except BlockingIOError:
            pass
        self.client.panel.socketToServer.send(struct.pack('!ii', 1, room_id))
        info = struct.unpack('!fff', self.client.panel.socketToServer.recv(12))
        if int(info[0]) == -1:
            self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("No valid bill to show yet..."))
        else:
            self.checkOutWindowContent.listWidget.addItem(QListWidgetItem("Total Fee: " + str(info[0])))
            self.checkOutWindowContent.listWidget.addItem(
                QListWidgetItem("Check-In Time: " + time.strftime("%Y-%m-%d %H:%M:%S",
                                                                  time.localtime(info[1]))))
            self.checkOutWindowContent.listWidget.addItem(QListWidgetItem(
                "Check-Out Time: " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(info[2]))))

    def save_report(self):
        # Todo:
        info_list = []
        for i in range(self.checkOutWindowContent.listWidget.count()):
            item = self.checkOutWindowContent.listWidget.item(i)
            info_list.append(item.text())
        with open('/home/zrg/test.csv', 'w') as f:
            f_csv = csv.writer(f)
            f_csv.writerow(
                ["Index", "Room ID", "Fan Speed", "Fee Rate", "Fee Sum", "Submit Time", "Start Time", "End Time",
                 "Start Temperature", "End Temperature"])
            for i in range(0, int(len(info_list) / 10)):
                f_csv.writerow([info_list[10 * i], info_list[10 * i + 1], info_list[10 * i + 2], info_list[10 * i + 3],
                                info_list[10 * i + 4],
                                info_list[10 * i + 5], info_list[10 * i + 6], info_list[10 * i + 7],
                                info_list[10 * i + 8],
                                info_list[10 * i + 9]])

    def confirm_check_out(self):
        # Todo:
        room_id = int(self.checkOutWindowContent.lineEdit.text())
        try:
            self.client.panel.socketToServer.recv(1024, socket.MSG_DONTWAIT)
        except BlockingIOError:
            pass
        self.client.panel.socketToServer.send(struct.pack('!ii', 3, room_id))
        outcome = struct.unpack('!i', self.client.panel.socketToServer.recv(256))
        if outcome[0] == 0:
            self.client.checkOut.checkOutWindow.setEnabled(False)
            self.client.errorDialog.show_dialog("Room doesn't exist!")
        else:
            self.client.checkOut.checkOutWindow.setEnabled(False)
            self.client.errorDialog.show_dialog("Successfully check out!")


class ClientRoomAssign:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.assignWindow = SubPanelWidget(client)
        self.assignWindowContent = FrontAssignUi()
        self.assignWindowContent.setupUi(self.assignWindow)
        self.assignWindow.is_accepted = False

        self.assignWindowContent.acceptButton.clicked.connect(self.assignWindow.close)

    def show_assign(self):
        self.assignWindow.show()


class PanelWidget(QWidget):
    def __init__(self, client):
        super().__init__()
        assert isinstance(client, Client)
        self.client = client

    def closeEvent(self, evt) -> None:
        self.client.login.show_login()
        self.client.panel.socketToServer.close()
        self.client.panel.socketToServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        pass


class ClientControlPanel:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.controlPanel = PanelWidget(client)
        self.controlPanelContent = FrontControlPanelUi()
        self.controlPanelContent.setupUi(self.controlPanel)
        self.socketToServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # self.controlPanelContent.pushButton.clicked.connect(self.report_request)
        self.controlPanelContent.logoutButtuon.clicked.connect(self.controlPanel.close)
        self.controlPanelContent.checkinButton.clicked.connect(self.room_assign)
        self.controlPanelContent.checkoutButton.clicked.connect(self.check_out)

    def socket_connect(self):
        try:
            # Todo: Change the address to central server
            self.socketToServer.connect((socket.gethostname(), 8000))
            try:
                self.socketToServer.recv(1024, socket.MSG_DONTWAIT)
            except BlockingIOError:
                pass
            self.socketToServer.send(struct.pack('!iii', 1, 0, 0))
            print("OK1")
            outcome = self.socketToServer.recv(256).decode()
            print("OK2")
            print(outcome)
            if outcome == 'Success':
                print("Success to connect to server!")
            else:
                print("Fail to connect to server!")
        except socket.error as e:
            print(e)
            return False
        else:
            return True

    def show_panel(self):
        self.controlPanel.show()

    def room_assign(self):
        # Todo: Make room distribution request
        self.controlPanel.setEnabled(False)
        self.client.checkIn.show_assign()
        try:
            self.socketToServer.recv(1024, socket.MSG_DONTWAIT)
        except BlockingIOError:
            pass

        self.socketToServer.send(struct.pack('!ii', 0, 0))
        room_id = struct.unpack('!i', self.socketToServer.recv(256))
        self.client.checkIn.assignWindowContent.roomID.setText(str(room_id[0]))
        self.client.checkIn.assignWindowContent.roomPasswd.setText('88888888')

    def check_out(self):
        self.controlPanel.setEnabled(False)
        self.client.checkOut.show_window()
        pass


class ClientLogIn:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.logInWindow = QWidget()
        self.logInWindowContent = FrontLogInUi()
        self.logInWindowContent.setupUi(self.logInWindow)

        self.PasswordList = [('Jack', '123456'), ('Bob', '654321')]

        self.logInWindowContent.loginButtion.clicked.connect(client.login_request)

    def check_passwd(self):
        id_input = self.logInWindowContent.idEdit.text()
        passwd_input = self.logInWindowContent.passwdEdit.text()
        if (id_input, passwd_input) in self.PasswordList:
            return True
        else:
            return False

    def show_login(self):
        self.logInWindow.show()

    def close_login(self):
        self.logInWindowContent.idEdit.setText('')
        self.logInWindowContent.passwdEdit.setText('')
        self.logInWindow.close()


class Client:
    def __init__(self):
        self.login = ClientLogIn(self)
        self.panel = ClientControlPanel(self)
        self.errorDialog = ClientDialog(self)
        self.checkIn = ClientRoomAssign(self)
        self.checkOut = ClientCheckOut(self)

    def login_request(self):
        if self.login.check_passwd():
            if self.panel.socket_connect():
                self.login.close_login()
                self.panel.show_panel()
            else:
                self.login.logInWindow.setEnabled(False)
                self.errorDialog.show_dialog("Fail to connect to central server!")
        else:
            self.login.logInWindow.setEnabled(False)
            self.errorDialog.show_dialog("ID or Password Wrong!")

    def client_show(self):
        self.login.show_login()


if __name__ == '__main__':
    clientProcess = QApplication([])
    clientWindow = Client()
    clientWindow.client_show()
    clientProcess.exec_()
